package com.relaxed;


import com.relaxed.constants.Constants;
import com.relaxed.domain.FieldMeta;
import com.relaxed.enums.SqlTypeEnum;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Execute;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import java.io.*;
import java.util.*;

/**
 * @author Yakir
 * @Topic PojoSqlMojo
 * @Description https://maven.apache.org/guides/plugin/guide-java-plugin-development.html
 *              https://maven.apache.org/developers/mojo-api-specification.html
 * @date 2021/12/8 16:34
 * @Version 1.0
 */
@Mojo(name = "generate")
public class PojoSqlMojo extends AbstractMojo {


    @Parameter( property = "scanRootDir",defaultValue = "${project.basedir}",readonly = true)
    private File basedir;
    @Parameter( property = "targetDir",defaultValue = "${project.build.directory}", readonly = true )
    private File target;

    @Parameter( property = "tablePrefix" ,defaultValue = "")
    private String tablePrefix;



    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        File scanDir =   new File(basedir,"\\");
        JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder();
        javaProjectBuilder.addSourceTree(scanDir);
        List<String> realSqlList = new ArrayList<>();
        getLog().info("analysis start path "+scanDir.getAbsolutePath());
        for (JavaClass aClass : javaProjectBuilder.getClasses()) {

            String className = aClass.getName();
            getLog().info("analysis class "+className);
            Map<String, FieldMeta> fieldMetaMap=new HashMap<>();
            List<JavaField> fields = aClass.getFields();
            //1.解析字段包括字段注解
            getLog().info("analysis class doc "+className);
            for (JavaField field : fields) {
                String fieldName = field.getName().toLowerCase(Locale.ROOT);
                //字段类型String
                String fileType = field.getType().getName();
                List<DocletTag> tags = field.getTags();
                HashMap<String, String> tagMap = new HashMap<>();
                if (tags!=null){
                    for (DocletTag tag : tags) {
                        String key = tag.getName();
                        String value = tag.getValue();
                        tagMap.put(key, value);
                    }
                }
                String comment = field.getComment();
                SqlTypeEnum sqlType = SqlTypeEnum.getSqlType(fileType);
                if (sqlType==null){
                    continue;
                }
                FieldMeta fieldMeta = convertToFieldMeta(fieldName, tagMap, comment, sqlType);
                fieldMetaMap.put(fieldName,fieldMeta);
            }
            getLog().info("generate class sql"+className);
            //2.解析字段值转为sql
            String lowerUnderlineClassName = getStandardFields(className).trim();
            String tableName=StringUtils.isEmpty(tablePrefix)? lowerUnderlineClassName:tablePrefix+"_"+lowerUnderlineClassName;
            StringBuffer column = new StringBuffer();
            for (JavaField field : fields) {
                String fieldName = field.getName().toLowerCase(Locale.ROOT);
                FieldMeta fieldMeta = fieldMetaMap.get(fieldName);
                if (fieldMeta==null){
                    continue;
                }
                String sqlFieldType = fieldMeta.getSqlFieldType();
                Map<String, String> tag = fieldMeta.getTag();
                String replaceSqlType = tag.get(Constants.SQL_TYPE_DOC);
                String requiredParam = tag.get(Constants.SQL_REQUIRED_DOC);

                String realSqlType=StringUtils.isNoneEmpty(replaceSqlType)?replaceSqlType:sqlFieldType;
                String required=StringUtils.isEmpty(requiredParam)?" NULL ":(requiredParam.equals("true")?" NOT NULL ":" NULL");
                column.append("\n `"+getStandardFields(fieldName)+"`"+""+realSqlType+" "+required+" comment '"+ fieldMeta.getComment()+"' ,");
            }
            String columnSql = column.toString();
            StringBuffer sql = new StringBuffer();
            sql.append("\n -- 表 " + tableName + "\n")
                    .append("\n DROP TABLE IF EXISTS `" + tableName + "`; ")
                    .append(" \n CREATE TABLE `" + tableName + "`  (")
                    .append(" \n `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',")
                    .append(" " + columnSql)
                    .append(" \n PRIMARY KEY (`id`) USING BTREE,")
                    .append("\n INDEX `id`(`id`) USING BTREE")
                    .append(" \n ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci; \n");
            realSqlList.add(sql.toString());
        }
        File targetFile = new File(target, "generateText.sql");
        getLog().info("write start path "+targetFile.getAbsolutePath());
        FileWriter fileWriter=null;
        try {
             fileWriter = new FileWriter(targetFile);
            for (String text : realSqlList) {
                getLog().info("执行写入"+text);
                fileWriter.write(text);
                fileWriter.flush();
            }
        } catch (IOException e) {
            throw new MojoFailureException("generate-file exception", e);
        }finally {
            if (fileWriter!=null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    getLog().error(e);
                }
            }
        }


    }
    /**
     * validate java collection
     *
     * @param type java typeName
     * @return boolean
     */
    public static boolean isCollection(String type) {
        switch (type) {
            case "java.util.List":
            case "java.util.LinkedList":
            case "java.util.ArrayList":
            case "java.util.Set":
            case "java.util.TreeSet":
            case "java.util.HashSet":
            case "java.util.SortedSet":
            case "java.util.Collection":
            case "java.util.ArrayDeque":
            case "java.util.PriorityQueue":
                return true;
            default:
                return false;
        }
    }

    /**
     * Check if it is an map
     *
     * @param type java type
     * @return boolean
     */
    public static boolean isMap(String type) {
        switch (type) {
            case "java.util.Map":
            case "java.util.SortedMap":
            case "java.util.TreeMap":
            case "java.util.LinkedHashMap":
            case "java.util.HashMap":
            case "java.util.concurrent.ConcurrentHashMap":
            case "java.util.concurrent.ConcurrentMap":
            case "java.util.Properties":
            case "java.util.Hashtable":
                return true;
            default:
                return false;
        }
    }

    /**
     * 转换为标准等sql字段 例如 adminUser → admin_user
     *
     * @param str 转换为字符串的字段名
     * @return
     */
    public static String getStandardFields(String str) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            //非首字母，在大写字母A到Z之间
            if (i != 0 && (c >= 'A' && c <= 'Z')) {
                sb.append("_");
            }
            sb.append(c);
        }
        return sb.toString().toLowerCase(Locale.ROOT);
    }
    private FieldMeta convertToFieldMeta(String fieldName, HashMap<String, String> tagMap, String comment, SqlTypeEnum sqlType) {
        FieldMeta fieldMeta = new FieldMeta();
        fieldMeta.setFieldName(fieldName);
        fieldMeta.setFieldType(sqlType.getClazz());
        fieldMeta.setSqlFieldType(sqlType.getSqlType());
        fieldMeta.setComment(comment);
        fieldMeta.setTag(tagMap);
        return fieldMeta;
    }
}
